package kuzmenkov.oip.service.impl;

import kuzmenkov.oip.common.I18nTool;
import kuzmenkov.oip.model.MethodClass;
import kuzmenkov.oip.model.Module;
import kuzmenkov.oip.model.Producer;
import kuzmenkov.oip.model.dao.hibernate.MethodClassDao;
import kuzmenkov.oip.model.dao.hibernate.ModuleDao;
import kuzmenkov.oip.service.ModuleChoosingService;
import kuzmenkov.oip.test.TestTool;
import kuzmenkov.oip.test.service.ContactServiceImplTest;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.richfaces.model.TreeNode;
import org.richfaces.model.TreeNodeImpl;
import org.springframework.dao.DataAccessException;

import java.util.Set;
import java.util.logging.Logger;

/**
 * User: Michael Kuzmenkov
 * Date: 18.03.2009
 * Time: 16:55:03
 */
public class ModuleChoosingServiceImpl implements ModuleChoosingService {
    private static final Logger log = Logger.getLogger("global");

    private final boolean TEST_MODE = false;
    private ModuleDao moduleDao;
    private MethodClassDao methodClassDao;
    private I18nTool i18nTool;

    private Module currentModule;
    private MethodClass currentClass;
    private Producer currentProducer;
    private static final int MAX_MODULE_NAME_LENGTH = 30;


    public void setI18nTool(I18nTool i18nTool) {
        this.i18nTool = i18nTool;
    }

    /**
     * A root node of a module tree
     */

    private TreeNode<String> rootNode;

    /**
     * Initializes a module tree
     */
    private void initializeModuleTree() throws Exception {
        rootNode = createModuleTree();
    }

    public TreeNode<String> getRootNode() throws Exception {
        try {
            //if (rootNode == null) { because of i18n
            initializeModuleTree();
            //}
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(i18nTool.getMessage("ModuleChoosingServiceImpl.gettingRootNode"));
        }
        return rootNode;
    }


    public void setModuleDao(ModuleDao moduleDao) {
        this.moduleDao = moduleDao;
    }

    public void setMethodClassDao(MethodClassDao methodClassDao) {
        this.methodClassDao = methodClassDao;
    }

    public TreeNode<String> createModuleTree() throws Exception {
        return createModuleTree(ModuleChoosingService.ROOT_NODE_ID);
    }

    public String getModuleName() throws Exception {
        try {
            if (currentModule != null) {
                String parentModuleName = i18nTool.internationalizeMethod(currentModule, "getParentModule");
                String moduleName = i18nTool.internationalizeMethod(currentModule, "getName");
                if (parentModuleName == null)
                    return moduleName;
                else
                    return parentModuleName + ": " + moduleName;

            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(i18nTool.getMessage("ModuleChoosingServiceImpl.gettingModuleName"));
        }
        return null;
    }

    public String getModuleDescription() throws Exception {
        try {
            if (currentModule != null)
                return i18nTool.internationalizeMethod(currentModule, "getDescription");
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(i18nTool.getMessage("ModuleChoosingServiceImpl.gettingModuleDescription"));
        }
        return null;
    }


    public void setCurrentModule(Long id) throws Exception {
        try {
            this.currentClass = null;
            this.currentModule = moduleDao.read(id);

            if (currentModule != null)
                setCurrentProducer(currentModule.getProducer());

            if (TEST_MODE){
                Result result = JUnitCore.runClasses(ContactServiceImplTest.class);//TODO
                TestTool.print(result);
            }
        } catch (DataAccessException e) {
            e.printStackTrace();
            throw new Exception(i18nTool.getMessage("ModuleChoosingServiceImpl.settingCurrentModule"));
        }

    }

    private void setCurrentProducer(Producer p) {
        currentProducer = p;
    }

    public boolean isModuleSelection() {
        return currentModule != null;
    }

    public boolean isClassSelection() {
        return currentClass != null;
    }

    public void setCurrentClass(Long id) throws Exception {
        try {
            this.currentModule = null;
            this.currentProducer = null;

            currentClass = methodClassDao.read(id);
        } catch (DataAccessException e) {
            e.printStackTrace();
            throw new Exception(i18nTool.getMessage("ModuleChoosingServiceImpl.settingCurrentClass"));
        }


    }

    public String getClassName() throws Exception {
        try {
            if (currentClass != null)
                return i18nTool.internationalizeMethod(currentClass, "getName");
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(i18nTool.getMessage("ModuleChoosingServiceImpl.gettingClassName"));
        }
        return null;
    }

    public String getClassDescription() throws Exception {
        try {
            if (currentClass != null)
                return i18nTool.internationalizeMethod(currentClass, "getDescription");
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(i18nTool.getMessage("ModuleChoosingServiceImpl.gettingClassDescription"));
        }
        return null;
    }

    public String getModuleProducerName() throws Exception {
        try {
            if (currentProducer != null)
                return i18nTool.internationalizeMethod(currentProducer, "getName");
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(i18nTool.getMessage("ModuleChoosingServiceImpl.gettingProducerName"));
        }
        return null;
    }


    public Module getCurrentModule() {
        return currentModule;
    }


    public TreeNode<String> createModuleTree(Long classId) throws Exception {
        TreeNode<String> root = null;
        try {
            root = new TreeNodeImpl<String>();
            MethodClass rootClass = methodClassDao.read(classId);
            Set<MethodClass> subclasses = rootClass.getSubclasses();
            if (subclasses != null) {
                attachSubclasses(root, subclasses);
            }
            Set<Module> modules = rootClass.getModules();
            if (modules != null) {
                attachModules(root, modules);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(i18nTool.getMessage("ModuleChoosingServiceImpl.creatingModuleTree"));
        }
        return root;

    }


    private void attachModules(TreeNode<String> node, Set<Module> items) throws Exception {
        for (Module item : items) {
            Long id = item.getModuleId();
            String parentModule = i18nTool.internationalizeMethod(item, "getParentModule");
            String moduleName = i18nTool.internationalizeMethod(item, "getName");
            String data = (parentModule != null ? parentModule + ": " : "") + moduleName;
            data = truncateString(data, MAX_MODULE_NAME_LENGTH);

            TreeNode<String> n = new TreeNodeImpl<String>();
            n.setData(data);

            String key = "m" + id;
            node.addChild(key, n);
        }

    }


    private void attachSubclasses(TreeNode<String> node, Set<MethodClass> items) throws Exception {
        for (MethodClass item : items) {
            Long id = item.getMethodClassId();
            String data = i18nTool.internationalizeMethod(item, "getName");
            data = truncateString(data, MAX_MODULE_NAME_LENGTH);

            TreeNode<String> n = new TreeNodeImpl<String>();
            n.setData(data);

            Set<MethodClass> subclasses = item.getSubclasses();
            if (subclasses != null) {

                attachSubclasses(n, subclasses);
            }
            Set<Module> modules = item.getModules();
            if (modules != null) {
                attachModules(n, modules);
            }

            String key = "c" + id;
            node.addChild(key, n);
        }
    }


    private String truncateString(String str, int maxLength) {
        if (str == null || str.isEmpty() || maxLength < 1) {
            log.warning("Illegal parameters: str=" + str + ", maxLength=" + maxLength);
            return null;
        }
        if (str.length() > maxLength) {
            str = str.substring(0, maxLength).trim() + "...";
        }

        return str;
    }
}
